/*
 * interrupt_trap_handler.c
 *
 *  Created on: Jun 3, 2013
 *      Author: Sean
 */

#include "interrupt_trap_handler.h"
#include "global.h"
#include "process.h"
#include <stdio.h>

int timer_int_handler(PCBPtr* process_ptr, int counter) {
        int error_val;
        PCBPtr process = *process_ptr;
        if(process == NULL) {
                // do nothing with idle processes--CPU will fire up a process if none are running.
                error_val = NULL_POINTER_ERROR;
        } else {
                process->setState(process, INTERRUPTED);
                if(!ready_queue->empty(ready_queue)) {
                  (*process_ptr) = ready_queue->dequeueFromQ(ready_queue);
                  ready_queue->addToQ(ready_queue, process);
                  printw("Timer Interrupt - P%d swapped out for P%d\n", process->pid, (*process_ptr)->pid);
                } else {
                  printw("Timer Interrupt - P%d continues\n", process->pid);
                }
                process->setState(process, RUNNING);
                
    //TODO: Context Switch
                error_val = NO_ERROR;
        }
        return error_val;
}

int io_int_handler(PCBPtr* process_ptr, int counter) {
        int error_val;
        PCBPtr process = *process_ptr;
        if(process == NULL) {
                printw("[err]interrupt handler with null process pointer\n");
                error_val = NULL_POINTER_ERROR;
        } else {
//                process->setIOQueue(process, process);
                process->setState(process, INTERRUPTED);
                //process->dequeueReadyQueue(process); //this is not what should happen
                if(!waiting_queue->empty(waiting_queue)) {
                  PCBPtr process2 = waiting_queue->dequeueFromQ(waiting_queue);
                  ready_queue->addToQ(ready_queue, process2);
                  ready_queue->addToQ(ready_queue, process2);
//    process->setReadyQueue(process,process2); //add to ready queue
                  process->setState(process, RUNNING);
                  printw("I/Oint - P%d continues, P%d notified and added to ready queue\n", process->pid, process2->pid);
                } else {
                  printw("I/Oint - No waiting process\n");
                }
    //TODO: this should probably have two separate waiting queues, one for kbd and one for others
                error_val = NO_ERROR;
        }
        return error_val;
}

int io_kybd_handler(PCBPtr* process_ptr, int counter) {
        int error_val;
        PCBPtr process = *process_ptr;
        if(process == NULL) {
//                printw("[err]interrupt handler with null process pointer\n");
                error_val = NULL_POINTER_ERROR;
        } else {
//                process->setIOQueue(process, process);
                process->setState(process, INTERRUPTED);
                keyboard_queue->addToQ(keyboard_queue, process);
                if(!ready_queue->empty(ready_queue)) {
                  PCBPtr process2 = ready_queue->dequeueFromQ(ready_queue);
                  (*process_ptr) = process2;
                  process->setState(process, RUNNING);
                  printw("I/Okbd - P%d added to IO Queue, P%d running\n", process->pid, process2->pid);
                } else {
                  (*process_ptr) = NULL;
                  printw("I/Okbd - No waiting process, idling\n");
                }
                error_val = NO_ERROR;
        }
        return error_val;
}

int io_req_trap_handler(PCBPtr* process_ptr, int counter) {
        int error_val;
        PCBPtr process = *process_ptr;
        if(process == NULL) {
 //               printw("[err]interrupt handler with null process pointer\n");
                error_val = NULL_POINTER_ERROR;
        } else {
				waiting_queue->addToQ(waiting_queue, process);
 //               process->setIOQueue(process, process);
                process->setState(process, INTERRUPTED);
                PCBPtr process2 = ready_queue->dequeueFromQ(ready_queue);
                (*process_ptr) = process2;
    //TODO: this ^ needs to be swapped with the current running process
                process->setState(process, RUNNING);
				printw("I/Oreq - P%d added to IO Queue, P%d running\n", process->pid, process2->pid);
                error_val = NO_ERROR;
        }
        return error_val;
}

int mutex_lock_trap_handler(PCBPtr* process_ptr, int counter){
        PCBPtr process = *process_ptr;
  if(process->shared) {
    Mutex* mutex_ptr = &(process->shared->lock);
    mutex_ptr->lock(mutex_ptr, process);
    //SCHEDULING
    //if the current process owns it, continue onward
    //if not, start running a new process (this one is already on the mutex's queue)
    if(mutex_ptr->locked != process) {
      PCBPtr process2 = ready_queue->dequeueFromQ(ready_queue);
      (*process_ptr) = process2;
      printw("M%d - P%d owns, P%d waiting, switching to P%d\n",process->shared->pair_number,mutex_ptr->locked->pid, process->pid, process2->pid);
    } else {
      printw("M%d - P%d locked, continuing\n",process->shared->pair_number,mutex_ptr->locked->pid, process->pid);
    }
  } else {
 //   printw("[err]mutex_lock_trap_handler called on non-shared process\n");
    return 1;
  }
}

int mutex_unlock_trap_handler(PCBPtr* process_ptr, int counter) {
//printw("[err]\nWE ARE GOING TO DIE!\n");
    PCBPtr process = *process_ptr;
	if(process->shared) {
//printw("[err]we must share...");
    Mutex* mutex_ptr = &(process->shared->lock);
//printw("[err]mutex pointer has been set.\n");
    mutex_ptr->unlock(mutex_ptr, process);		//TODO -- we segfault here.
//printw("[err]mutex has been unlocked.");
    //SCHEDULING
    //continue running this process
    //place the popped process on the ready queue if there is one
    if(mutex_ptr->locked) {

      ready_queue->addToQ(ready_queue, process);
      printw("M%d - P%d unlocked, P%d added to ready queue\n",process->shared->pair_number,process->pid,mutex_ptr->locked->pid);
    } else {
      (*process_ptr) = NULL;
      printw("M%d - P%d unlocked, none waiting\n",process->shared->pair_number,process->pid);
    }
  } else {
    printw("[err]mutex_lock_trap_handler called on non-shared process\n");
    return 1;
  }
//printw("[err]\nI regret nothing!\n");
}

int cond_wait_trap_handler(PCBPtr* process_ptr, int counter) {
        PCBPtr process = *process_ptr;
  if(process->shared) {
    Mutex* mutex_ptr = &(process->shared->lock);
    ConditionVariable* full_ptr = &(process->shared->full);
    ConditionVariable* empty_ptr = &(process->shared->empty);
    if(process->prod_cons_type == 'p') {
      full_ptr->wait(full_ptr,mutex_ptr,process); //wait for it to be not full
    } else if(process->prod_cons_type == 'c') {
      empty_ptr->wait(empty_ptr,mutex_ptr,process); //wait for it to be not empty
    } else { //invalid type
      printw("[err]cond_wait_trap_handler process has invalid type");
    }
    //SCHEDULING
    //select a new process (this one is now waiting)
    //TODO: this may be incorrect
    if(!ready_queue->empty(ready_queue)) {
      (*process_ptr) = ready_queue->dequeueFromQ(ready_queue);
      printw("CV%d%c wait - P%d waiting, switching to P%d\n",
                  process->shared->pair_number,
                  process->prod_cons_type,
                  process->pid,
                  (*process_ptr)->pid);
    } else {
      printw("CV%d%c wait - P%d waiting, nothing to switch to\n",
                  process->shared->pair_number,
                  process->prod_cons_type,
                  process->pid);
    }
  } else {
    printw("[err]cond_wait_trap_handler called on non-shared process\n");
    return 1;
  }
}

int cond_signal_trap_handler(PCBPtr* process_ptr, int counter) {
        PCBPtr process = *process_ptr;
  if(process->shared) {
    Mutex* popped_mutex;
    PCBPtr* popped_process;
    ConditionVariable* full_ptr = &(process->shared->full);
    ConditionVariable* empty_ptr = &(process->shared->empty);
    if(process->prod_cons_type == 'p') {
      empty_ptr->notify(empty_ptr,process,popped_process,popped_mutex); //notify that it is not empty
    } else if(process->prod_cons_type == 'c') {
      full_ptr->notify(full_ptr,process,popped_process,popped_mutex); //notify that it is not full
    } else { //invalid type
      printw("[err]cond_wait_trap_handler process has invalid type");
    }
    //SCHEDULING
    //if the popped process owns the associated mutex, place it on the ready queue
    //continue running this process
    //TODO: may not be valid
    if(popped_process && popped_mutex->lock == *popped_process) {
      printw("CV%d%c notify - P%d notified, requesting M%d, added to ready queue, continuing P%d\n",
                process->shared->pair_number,
                process->prod_cons_type,
                (*popped_process)->pid,
                process->shared->pair_number,
                process->pid);
      ready_queue->addToQ(ready_queue,popped_process);
    } else {
      printw("CV%d%c notify - no process to notify, continuing P%d\n",
                process->shared->pair_number,
                process->prod_cons_type,
                process->pid);
    }
  } else {
    printw("[err]cond_wait_trap_handler called on non-shared process\n");
    return 1;
  }
}
